﻿// Inner Fire 游戏引擎库
// VertexList - 顶点列表
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 用于统一管理顶点列表的项目。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-03-19

#pragma once

#include <EASTL/vector.h>
#include <assimp/scene.h> // 包含Assimp的场景结构体
#include <cstdint>
#include <dx/core/dx_core.h>
#include <dx/mesh/dx_mesh.h>
#include <static_string.h>

namespace ifire {

template <typename T> class VertexList {
public:
  VertexList() = default;
  VertexList(const VertexList& rhs) = delete;
  VertexList& operator=(const VertexList& rhs) = delete;
  ~VertexList() = default;

  //
  // 属性访问
  //
  dx::DxMesh* Mesh() const { return mesh_.get(); }
  // 在项目后面增加一个项目
  inline T& AddVertex() { return vertices_.emplace_back(); }
  inline void AddVertex(const T& t) { vertices_.push_back(t); }
  // 增加一个索引
  inline void AddIndex(uint32_t value) { indices_.push_back(value); }
  eastl::vector<T>& Vertices() { return vertices_; }
  eastl::vector<uint32_t>& Indices() { return indices_; }

  // 通过SubMesh的Index和项目的Vertex，获得一个顶点
  T& GetVertex(UINT submesh_index, UINT vertex_index){
    auto& submesh = mesh_->Submeshs()[submesh_index];
    UINT point = submesh.vertex_start + vertex_index;
    return vertices_[point];
  }

  // 直接生成项目
  void Init(ikit::sstr name) {
    // 初始化Mesh
    mesh_ = std::make_unique<dx::DxMesh>(0, name);
  }

  // 通过一个Scene分配项目
  void Init(const aiScene* scene, ikit::sstr name) {
    // 初始化Mesh
    mesh_ = std::make_unique<dx::DxMesh>(0, name);
    // 对Mesh进行计算，以预留好分配的空间
    UINT vertex_count = 0;
    UINT index_count = 0;
    for (UINT i = 0; i < scene->mNumMeshes; i++) {
      auto& mesh = scene->mMeshes[i];
      // 将当前的项目填充进SubMesh
      UINT mesh_index_count = mesh->mNumFaces * 3; // 每面3索引
      mesh_->AddSubmesh(mesh->mName.C_Str(), mesh->mMaterialIndex, index_count,
          mesh_index_count, vertex_count, mesh->mNumVertices);
      // 累加项目
      vertex_count += mesh->mNumVertices;
      index_count += mesh_index_count;
    }

    // 将预留的空间配置
    vertices_.reserve(vertex_count);
    indices_.reserve(index_count);
  }

  // 读取一个Mesh
  void ReadMesh(const aiMesh* mesh, bool convert_units_) {
    constexpr float UNITS = 0.01f;

    // 读入顶点（这里就是c++的泛型和其它语言最大的不同！）
    for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
      auto& vertex = AddVertex();

      auto pos = mesh->mVertices[i];

      if (convert_units_){
        vertex.pos = DirectX::XMFLOAT3(pos.x * UNITS, pos.y * UNITS, pos.z * UNITS);
      }else{
        vertex.pos = DirectX::XMFLOAT3(pos.x, pos.y, pos.z);
      }
      

      if (mesh->mNormals) {
        auto& normal = mesh->mNormals[i];
        vertex.normal = DirectX::XMFLOAT3(normal.x, normal.y, normal.z);
      } else {
        vertex.normal = DirectX::XMFLOAT3(0.0f, 1.0f, 0.0f);
      }

      // 假设纹理坐标和切线存在
      if (mesh->HasTextureCoords(0)) {
        auto& texcoord = mesh->mTextureCoords[0][i];
        vertex.texcoord = DirectX::XMFLOAT2(texcoord.x, texcoord.y);
      } else {
        vertex.texcoord = DirectX::XMFLOAT2(0.0f, 0.0f);
      }

      if (mesh->mTangents) {
        auto& tangent = mesh->mTangents[i];
        vertex.tangent = DirectX::XMFLOAT3(tangent.x, tangent.y, tangent.z);
      } else {
        vertex.tangent = DirectX::XMFLOAT3(0.0f, 0.0f, 0.0f);
      }
    }

    // 读取索引
    for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
      const aiFace& face = mesh->mFaces[i];
      for (unsigned int j = 0; j < face.mNumIndices; j++) {
        AddIndex(face.mIndices[j]);
      }
    }
  }

  void CreateResource(
      ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list) {
    // 创建Mesh的资源
    dx::DxMeshCreateDesc desc{};
    desc.pVertexData = vertices_.data();
    desc.pIndexData = indices_.data();
    desc.VertexSize = vertices_.size();
    desc.VertexStrideSize = sizeof(T);
    desc.IndexSize = indices_.size();
    desc.IndexFormat = DXGI_FORMAT_R32_UINT;
    desc.IndexStrideSize = sizeof(std::uint32_t);
    mesh_->CreateResource(device, cmd_list, desc);
    
    // TODO：清空顶点。但这影响Uploader的操作，需要另外执行？
  }

  // 计算绑定盒子
  void CalculateBoundingBox() {
    for (auto& e : mesh_->Submeshs()) {
      // 这里是有一个BUG的，必须Position在第一位。
      T* pointer = vertices_.data();
      pointer += e.vertex_start;
      DirectX::BoundingBox::CreateFromPoints(e.bounding_box, e.vertex_count,
          (const DirectX::XMFLOAT3*)(pointer), sizeof(T));
    }
  }

  // 归一化顶点权重（有时候顶点权重配置不好，就得用这个）
  void NormalizeWeights(){
    for (auto& v : vertices_){
      v.NormalizeWeights();
    }
  }

private:
  dx::DxMeshPtr mesh_;
  eastl::vector<T> vertices_;
  eastl::vector<uint32_t> indices_;
};

} // namespace ifire